'''
数据背景：某班30名学生的语文、数学、英语三科期末考试成绩(满分150分），语文成绩：90-135分，中位数110，四分位数Q1=100、Q3=120，无异常值；
数学成绩：85-140分，中位数115，四分位数Q1=95、Q3=125，异常值145；英语成绩：80-130分，中位数105，四分位数Q1=90、Q3=115，无异常值。绘制
要求：在同一张图中绘制三科成绩的箱线图，以“科目”为X轴，“成绩”为Y轴，异常值用红色圆点标记，添加图表标题“语文、数学、英语成绩箱线图”。分析目
标：对比三科成绩的离散程度、中位数水平，识别数学成绩中的异常值情况。
'''
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.font_manager as fm

# 设置中文字体
plt.rcParams['font.sans-serif'] = ['SimHei']  # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False  # 用来正常显示负号


# 根据给定的统计量生成模拟数据
def generate_subject_data(min_val, q1, median, q3, max_val, outlier=None, n=30):
    """
    根据给定的统计量生成模拟数据
    min_val: 最小值
    q1: 第一四分位数
    median: 中位数
    q3: 第三四分位数
    max_val: 最大值
    outlier: 异常值（如果有）
    n: 数据点数量
    """
    # 创建四分位数的数据分布
    data = []

    # 第一四分位到最小值 (25%的数据)
    n_q1_min = n // 4
    data.extend(np.random.uniform(min_val, q1, n_q1_min))

    # 第一四分位到中位数 (25%的数据)
    n_q1_median = n // 4
    data.extend(np.random.uniform(q1, median, n_q1_median))

    # 中位数到第三四分位 (25%的数据)
    n_median_q3 = n // 4
    data.extend(np.random.uniform(median, q3, n_median_q3))

    # 第三四分位到最大值 (25%的数据)
    n_q3_max = n - len(data)
    data.extend(np.random.uniform(q3, max_val, n_q3_max))

    # 如果有异常值，替换最后一个值为异常值
    if outlier is not None:
        data[-1] = outlier

    return data


# 生成三科成绩数据
chinese_scores = generate_subject_data(90, 100, 110, 120, 135)  # 语文成绩
math_scores = generate_subject_data(85, 95, 115, 125, 140, outlier=145)  # 数学成绩，包含异常值
english_scores = generate_subject_data(80, 90, 105, 115, 130)  # 英语成绩

# 准备箱线图数据
data = [chinese_scores, math_scores, english_scores]
labels = ['语文', '数学', '英语']

# 创建画布
plt.figure(figsize=(10, 6), dpi=100)

# 绘制箱线图
box = plt.boxplot(
    data,
    labels=labels,
    showfliers=True,  # 显示异常值
    patch_artist=True,  # 填充箱体颜色
    widths=0.6,  # 箱体宽度
    # 异常值样式
    flierprops=dict(marker='o', markerfacecolor='red', markersize=8, linestyle='none'),
    # 箱体样式
    boxprops=dict(facecolor='lightblue', color='blue', linewidth=1.5),
    # 须线样式
    whiskerprops=dict(color='blue', linewidth=1.5),
    # 中位数线样式
    medianprops=dict(color='orange', linewidth=2),
    # 帽子样式
    capprops=dict(color='blue', linewidth=1.5)
)

# 设置图表属性
plt.title('语文、数学、英语成绩箱线图', fontsize=16, fontweight='bold', pad=20)
plt.xlabel('科目', fontsize=12)
plt.ylabel('成绩', fontsize=12)
plt.xticks(fontsize=12)
plt.yticks(fontsize=12)
plt.grid(True, alpha=0.3, linestyle='--')

# 添加数值标注
for i, (q1, median, q3) in enumerate([(100, 110, 120), (95, 115, 125), (90, 105, 115)]):
    plt.text(i + 1, q1 - 2, f'Q1={q1}', ha='center', va='top', fontsize=9, color='darkblue')
    plt.text(i + 1, median + 2, f'中位数={median}', ha='center', va='bottom', fontsize=9, color='darkorange')
    plt.text(i + 1, q3 + 2, f'Q3={q3}', ha='center', va='bottom', fontsize=9, color='darkblue')

# 显示图表
plt.tight_layout()
plt.show()

# 数据分析
print("=" * 50)
print("三科成绩分析报告")
print("=" * 50)

# 计算各项统计指标
chinese_stats = {
    'mean': np.mean(chinese_scores),
    'std': np.std(chinese_scores),
    'min': np.min(chinese_scores),
    'max': np.max(chinese_scores),
    'range': np.max(chinese_scores) - np.min(chinese_scores)
}

math_stats = {
    'mean': np.mean(math_scores),
    'std': np.std(math_scores),
    'min': np.min(math_scores),
    'max': np.max(math_scores),
    'range': np.max(math_scores) - np.min(math_scores)
}

english_stats = {
    'mean': np.mean(english_scores),
    'std': np.std(english_scores),
    'min': np.min(english_scores),
    'max': np.max(english_scores),
    'range': np.max(english_scores) - np.min(english_scores)
}

print("\n1. 离散程度分析:")
print(f"   语文: 标准差 = {chinese_stats['std']:.2f}, 极差 = {chinese_stats['range']:.2f}")
print(f"   数学: 标准差 = {math_stats['std']:.2f}, 极差 = {math_stats['range']:.2f}")
print(f"   英语: 标准差 = {english_stats['std']:.2f}, 极差 = {english_stats['range']:.2f}")

print("\n2. 中位数水平对比:")
print(f"   语文中位数: {np.median(chinese_scores):.2f}")
print(f"   数学中位数: {np.median(math_scores):.2f}")
print(f"   英语中位数: {np.median(english_scores):.2f}")

print("\n3. 数学成绩异常值分析:")
math_outliers = [x for x in math_scores if x > 125 + 1.5 * (125 - 95)]  # 使用Tukey方法识别异常值
print(f"   数学成绩异常值: {math_outliers}")
print(f"   异常值数量: {len(math_outliers)}")
print(f"   异常值可能原因: 可能是学生发挥超常或存在特殊情况")

print("\n4. 综合对比:")
print(
    f"   中位数最高科目: {'数学' if np.median(math_scores) > np.median(chinese_scores) and np.median(math_scores) > np.median(english_scores) else '语文' if np.median(chinese_scores) > np.median(english_scores) else '英语'}")
print(
    f"   离散程度最大科目: {'数学' if math_stats['std'] > chinese_stats['std'] and math_stats['std'] > english_stats['std'] else '语文' if chinese_stats['std'] > english_stats['std'] else '英语'}")